home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Games / Xconq 7.0d16 / Xconq 7.0d16 src / mac / macwins.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-07  |  52.1 KB  |  1,950 lines  |  [TEXT/KAHL]

  1. /* Copyright (c) 1992, 1993 Stanley T. Shebs. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. /* Handling of assorted minor windows for the Mac interface. */
  6.  
  7. #include "conq.h"
  8. #include "mac.h"
  9.  
  10. /* #include <time.h> */
  11.  
  12. HelpNode *find_help_node();
  13.  
  14. Unit *get_selected_construction_unit();
  15.  
  16. extern time_t realtimeturnplaystart;
  17. extern time_t realtimegamestart;
  18.  
  19. char *unit_namer();
  20. char *propose_unit_name();
  21. UnitVector *add_unit_to_vector();
  22.  
  23. /* Globals for the instructions window. */
  24.  
  25. WindowPtr instructionswin = nil;
  26.  
  27. TEHandle instructionstext = nil;
  28.  
  29. /* Globals for the game window. */
  30.  
  31. WindowPtr gamewin = nil;
  32.  
  33. int gamewinw = 200;
  34. int gametoph = 32;
  35. int gametoptoph = 18;
  36. int gamesidehgt = 24;
  37.  
  38. Handle aisicnhandle = nil;
  39. Handle facesicnhandle[3];
  40.  
  41. char *gameprogressstr = "";
  42.  
  43. time_t lastnow;
  44.  
  45. /* Globals for the construction window. */
  46.  
  47. WindowPtr constructionwin = nil;
  48.  
  49. ListHandle constructionunitlist = nil;
  50. ListHandle constructiontypelist = nil;
  51.  
  52. int constructmargin = 5;
  53. int constructtop = 32;
  54.  
  55. /* This is the vector of units that can do construction or research. */
  56.  
  57. UnitVector *possibleunits = NULL;
  58.  
  59. int numposstypes;
  60. int *possibletypes = NULL;
  61.  
  62. int currunlength = 99;
  63.  
  64. ControlHandle constructbutton;
  65. ControlHandle researchbutton;
  66.  
  67. TEHandle runlengthtext = nil;
  68.  
  69. Rect unitlistrect, typelistrect;
  70.  
  71. /* Globals for unit closeup windows. */
  72.  
  73. int lastunitcloseuph = -1, lastunitcloseupv = -1;
  74.  
  75. /* Globals for the history window. */
  76.  
  77. WindowPtr historywin = nil;
  78.  
  79. HistEvent **histcontents = NULL;
  80.  
  81. int numhistcontents = 0;
  82.  
  83. ControlHandle histvscrollbar;
  84.  
  85. int histlinespacing = 15; /* (should derive from font) */
  86.  
  87. HistEvent *firstvisevt = NULL;
  88.  
  89. int numvisevents;
  90.  
  91. int numvishistlines;
  92.  
  93. int totalhistlines = 0;
  94.  
  95. /* Globals for the help window. */
  96.  
  97. DialogPtr helpwin = nil;
  98.  
  99. TEHandle helptopic = nil;
  100.  
  101. TEHandle helptext = nil;
  102.  
  103. ControlHandle helpvscrollbar;
  104. ControlHandle helphscrollbar;
  105.  
  106. ControlHandle topicsbutton;
  107. ControlHandle helpbutton;
  108. ControlHandle prevbutton;
  109. ControlHandle nextbutton;
  110. ControlHandle backbutton;
  111.  
  112. struct a_helpnode *curhelpnode = NULL;
  113. char *helpstring = NULL;
  114. int helpscreentop = 0;
  115.  
  116. /* The help node that tells about how to use the help system. */
  117.  
  118. HelpNode *helphelpnode = NULL;
  119.  
  120. #define NODESTACKSIZE 50
  121.  
  122. HelpNode **nodestack;
  123.  
  124. int nodestackpos;
  125.  
  126. /* The instructions window. */
  127.  
  128. pascal void
  129. draw_instructions_text(win, ditem)
  130. WindowPtr win;
  131. short ditem;
  132. {
  133.     GrafPtr oldport;
  134.     short itemtype;  Handle itemhandle;  Rect itemrect;
  135.  
  136.     GetDItem(instructionswin, diInstructionsText, &itemtype, &itemhandle, &itemrect);
  137.     GetPort(&oldport);
  138.     SetPort(instructionswin);
  139.     TextSize(14);
  140.     TEUpdate(&itemrect, instructionstext);
  141.     /* This makes the title item draw big. */
  142.     TextSize(18);
  143.     SetPort(oldport);
  144. }
  145.  
  146. /* Bring up the dialog with instructions on how to play. */
  147.  
  148. instructions_dialog()
  149. {
  150.     if (instructionswin == nil) {
  151.         create_instructions_dialog();
  152.     }
  153.     ShowWindow(instructionswin);
  154.     SelectWindow(instructionswin);
  155. }
  156.  
  157. create_instructions_dialog()
  158. {
  159.     Obj *instructions, *rest;
  160.     char *str;
  161.     Str255 tmpstr;
  162.     Rect destrect, viewrect;
  163.     short itemtype;  Handle itemhandle;  Rect itemrect;
  164.     extern Module *selectedgame;
  165.  
  166.     instructionswin = GetNewDialog(dInstructions, nil, (DialogPtr) -1L);
  167.     GetDItem(instructionswin, diInstructionsTitle, &itemtype, &itemhandle, &itemrect);
  168.     c2p(((selectedgame && selectedgame->title) ? selectedgame->title : ""), tmpstr);
  169.     SetIText(itemhandle, tmpstr);
  170.     GetDItem(instructionswin, diInstructionsText, &itemtype, &itemhandle, &itemrect);
  171.     SetDItem(instructionswin, diInstructionsText, itemtype, (Handle) draw_instructions_text, &itemrect);
  172.     destrect = itemrect;
  173.     viewrect = itemrect;
  174.     SetPort(instructionswin);
  175.     /* All text will be in Times. */
  176.     /* (should put sizes in a resource so can be edited) */
  177.     TextFont(times);
  178.     TextSize(14);
  179.     instructionstext = TENew(&destrect, &viewrect);
  180.     TESetSelect(0, 32767, instructionstext);
  181.     TEDelete(instructionstext);
  182.     if (selectedgame && (instructions = selectedgame->instructions) != lispnil) {
  183.         if (stringp(instructions)) {
  184.             str = c_string(instructions);
  185.             TEInsert(str, strlen(str), instructionstext);
  186.         } else {
  187.             for (rest = instructions; rest != lispnil; rest = cdr(rest)) {
  188.                 if (stringp(car(rest))) {
  189.                     str = c_string(car(rest));
  190.                     /* An empty line is a paragraph break. */
  191.                     if (strlen(str) == 0) {
  192.                         str = "\r\r";
  193.                     }
  194.                     TEInsert(str, strlen(str), instructionstext);
  195.                     TESetSelect(100000, 100000, instructionstext);
  196.                     /* Always insert a blank between strings, since they are
  197.                        usually word breaks. */
  198.                     TEInsert(" ", 1, instructionstext);
  199.                     TESetSelect(100000, 100000, instructionstext);
  200.                 }
  201.             }
  202.         }
  203.     } else {
  204.         str = "(no instructions available)";
  205.         TESetText(str, strlen(str), instructionstext);
  206.     }
  207.     /* This makes the title item draw big. */
  208.     TextSize(18);
  209.     add_window_menu_item("Instructions", instructionswin);
  210. }
  211.  
  212. hit_instructions_dialog(dialog, itemhit, evt)
  213. DialogPtr dialog;
  214. short itemhit;
  215. EventRecord *evt;
  216. {
  217.     short itemtype;  Handle itemhandle;  Rect itemrect;
  218.     
  219.     switch (itemhit) {
  220.         case diInstructionsHelp:
  221.             /* Just jump to the help dialog. */
  222.             help_dialog();
  223.             break;
  224.     }
  225.     return TRUE;
  226. }
  227.  
  228. /* The game progress window. */
  229.  
  230. /* Create the game progress window. */
  231.  
  232. create_game_window()
  233. {
  234.     extern int numscores;
  235.  
  236.     /* Create the window, color if possible, since emblems may be in color. */
  237.     if (hasColorQD) {    
  238.         gamewin = GetNewCWindow(wGame, NULL, (WindowPtr) -1L);
  239.     } else {
  240.         gamewin = GetNewWindow(wGame, NULL, (WindowPtr) -1L);
  241.     }
  242.     /* Add an additional row of space for each two scorekeepers. */
  243.     if (keeping_score()) {
  244.         gamesidehgt += 15 * ((numscorekeepers + 1) / 2);
  245.     }
  246.     /* (should allocate extra display width if game is realtime per side) */
  247.     /* This is not growable, so we have to ensure it's big enough to start with. */
  248.     SizeWindow(gamewin, gamewinw, numsides * gamesidehgt + gametoph, 1);
  249.     /* Get handles to useful sicns. */
  250.     aisicnhandle = GetNamedResource('SICN', "\pmplayer");
  251.     facesicnhandle[0] = GetNamedResource('SICN', "\phostile");
  252.     facesicnhandle[1] = GetNamedResource('SICN', "\pneutral");
  253.     facesicnhandle[2] = GetNamedResource('SICN', "\pfriendly");
  254. }
  255.  
  256. draw_game()
  257. {
  258.     Side *side2;
  259.     GrafPtr oldport;
  260.  
  261.     if (gamewin == nil) return;
  262.     GetPort(&oldport);
  263.     SetPort(gamewin);
  264.     EraseRect(&gamewin->portRect);
  265.     draw_game_date();
  266.     draw_game_progress();
  267.     /* Draw a solid separating line between date info and side list. */
  268.     MoveTo(0, gametoph);
  269.     Line(gamewin->portRect.right, 0);
  270.     for_all_sides(side2) {
  271.         draw_game_side(side2);
  272.     }
  273.     SetPort(oldport);
  274. }
  275.  
  276. /* Display the current time and date and any realtime countdowns. */
  277.  
  278. draw_game_date()
  279. {
  280.     Rect tmprect;
  281.     extern int endofgame;
  282.  
  283.     SetRect(&tmprect, 0, 0, gamewin->portRect.right, gametoptoph - 1);
  284.     EraseRect(&tmprect);
  285.     MoveTo(10, 12);
  286.     TextFace(bold);
  287.     DrawString(curdatestr);
  288.     TextFace(0);
  289.     /* (should draw season name here somewhere?) */
  290.     draw_game_clocks();
  291.     if (endofgame) {
  292.         gray_out_rect(&tmprect);
  293.     }
  294. #ifdef DEBUGGING
  295.     /* Indicate the state of all the debug flags. */
  296.     if (Debug || DebugM || DebugG) {
  297.         sprintf(spbuf, "%c%c%c",
  298.                 (Debug ? 'D' : ' '), (DebugM ? 'M' : ' '), (DebugG ? 'G' : ' '));
  299.         MoveTo(tmprect.right - 30, 12);
  300.         DrawText(spbuf, 0, strlen(spbuf));
  301.     }
  302. #endif /* DEBUGGING */
  303. }
  304.  
  305. draw_game_clocks()
  306. {
  307.     int elapsed;
  308.     time_t now;
  309.     Str255 tmpstr;
  310.     Rect tmprect;
  311.  
  312.     if (g_rt_for_game() > 0) {
  313.         time(&now);
  314.         elapsed = (int) difftime(now, realtimegamestart);
  315.         time_desc(spbuf, g_rt_for_game() - elapsed, g_rt_for_game());
  316.         SetRect(&tmprect, 100, 0, 100 + 100 /*TextWidth(spbuf, 0, strlen(spbuf))*/, 10);
  317.         EraseRect(&tmprect);
  318.         MoveTo(100, 10);
  319.         DrawText(spbuf, 0, strlen(spbuf));
  320.         lastnow = now;
  321.     }
  322.     if (g_rt_per_turn() > 0) {
  323.         time(&now);
  324.         elapsed = (int) difftime(now, realtimeturnplaystart);
  325.         time_desc(spbuf, g_rt_per_turn() - elapsed, g_rt_per_turn());
  326.         SetRect(&tmprect, 100, 10, 100 + 100 /*TextWidth(spbuf, 0, strlen(spbuf))*/, 20);
  327.         EraseRect(&tmprect);
  328.         MoveTo(100, 20);
  329.         DrawText(spbuf, 0, strlen(spbuf));
  330.         lastnow = now;
  331.     }
  332.     /* (should draw per-side clocks) */
  333. }
  334.  
  335. draw_game_progress()
  336. {
  337.     Rect tmprect;
  338.  
  339.     SetRect(&tmprect, 0, gametoptoph, gamewin->portRect.right, gametoph - 1);
  340.     EraseRect(&tmprect);
  341.     MoveTo(1, gametoptoph + 12);
  342.     DrawText(gameprogressstr, 0, strlen(gameprogressstr));
  343. }
  344.  
  345. /* Draw info about a given side. */
  346.  
  347. draw_game_side(side2)
  348. Side *side2;
  349. {
  350.     int s2 = side_number(side2);
  351.     int sx = 20, sy = (s2 - 1) * gamesidehgt + gametoph;
  352.     Str255 tmpstr;
  353.     Rect tmprect;
  354.     RGBColor tmpcolor;
  355.  
  356. #if 0
  357.     /* (this should be added to emblem drawing as "shadow" option, and set up to use
  358.     the emblem's mask properly) */
  359.     /* Fill offset rect with gray, for contrast with white parts of emblem. */
  360.     SetRect(&tmprect, 2 + 1, sy + 4 + 1, 2 + 16 + 1, sy + 4 + 16 + 1);
  361.     if (hasColorQD) {
  362.         tmpcolor.red = tmpcolor.green = tmpcolor.blue = 49000;
  363.         RGBForeColor(&tmpcolor);
  364.         PaintRect(&tmprect);
  365.         /* Restore the previous color. */
  366.         tmpcolor.red = tmpcolor.green = tmpcolor.blue = 0;
  367.         RGBForeColor(&tmpcolor);
  368.     } else {
  369.         FillRect(&tmprect, QD(gray));
  370.     }
  371. #endif
  372.     draw_side_emblem(gamewin, 2, sy + 4, 16, 16, s2);
  373.     sprintf(spbuf, "%s", short_side_title(side2));
  374.     MoveTo(sx, sy + 12);
  375.     /* Put the name of our side in boldface. */
  376.     TextFace((side2 == dside ? bold : 0));
  377.     DrawText(spbuf, 0, strlen(spbuf));
  378.     TextFace(0);
  379.     if (side_has_ai(side2) && side2->ingame) {
  380.         /* Show that the side is run by an AI. */
  381.         plot_sicn(gamewin, 182, sy + 2, aisicnhandle, 0, TRUE);
  382.     }
  383.     if (side2 != dside && side2->ingame && side_has_ai(side2) /* doesn't account for other humans */) {
  384.         /* Indicate attitude of other side. */
  385.         plot_sicn(gamewin, 164, sy + 2,
  386.             facesicnhandle[feeling_towards(side2, dside)], 0, TRUE);
  387.     }
  388.     draw_side_status(side2);
  389.     /* Draw a separating line. */
  390.     PenPat(QD(gray));
  391.     MoveTo(0, sy + gamesidehgt);
  392.     Line(gamewin->portRect.right, 0);
  393.     PenNormal();
  394. }
  395.  
  396. feeling_towards(side, side2)
  397. Side *side, *side2;
  398. {
  399.     if (side_has_ai(side) && should_try_to_win(side)) {
  400.         return 0;
  401.     } else {
  402.         return 1;
  403.     }
  404. }
  405.  
  406. /* Draw the current details about a side. */
  407.  
  408. draw_side_status(side2)
  409. Side *side2;
  410. {
  411.     int sx, sy = (side_number(side2) - 1) * gamesidehgt + gametoph, i, h, v;
  412.     int totacp, resvacp, acpleft, percentleft, percentresv;
  413.     Rect siderect, tmprect, progressrect;
  414.     extern int endofgame;
  415.     Scorekeeper *sk;
  416.  
  417.     SetRect(&siderect, 0, sy + 1, gamewin->portRect.right, sy + gamesidehgt);
  418.     if (!side2->ingame || endofgame) {
  419.         gray_out_rect(&siderect);
  420.         if (side_won(side2)) {
  421.             /* (should) Indicate that this side has won. */
  422.             /* draw like a trophy or flourishes or some such?) */
  423.         } else if (side_lost(side2)) {
  424.             /* Draw a line crossing out the loser.  Simple and obvious. */
  425.             MoveTo(1, sy + 8);  Line(gamewin->portRect.right - 3, 0);
  426.         }
  427.     } else {
  428.         /* Set up and clear the area where we show progress. */
  429.         SetRect(&progressrect, 20, sy + 12 + 4, 20 + 100, sy + 12 + 4 + 7);
  430.         EraseRect(&progressrect);
  431.         /* Show the current acp totals/progress of the side. */
  432.         /* This is not quite the security hole it might seem,
  433.            you don't get much value out of seeing how far along each side is. */
  434.         totacp = side_initacp(side2);
  435.         if (totacp > 0) {
  436.             FrameRect(&progressrect);
  437.             acpleft = side_acp(side2);
  438.             resvacp = side_acp_reserved(side2);
  439.             if (totacp > 0) {
  440.                 percentleft = (100 * acpleft) / totacp;
  441.                 percentleft = max(0, min(99, percentleft));
  442.                 percentresv = (100 * resvacp) / totacp;
  443.                 percentresv = max(0, min(99, percentresv));
  444.             } else {
  445.                 percentleft = percentresv = 0;
  446.             }
  447.             if (percentleft > 0) {
  448.                 tmprect = progressrect;
  449.                 InsetRect(&tmprect, 1, 1);
  450.                 tmprect.right = tmprect.left + percentleft;
  451.                 FillRect(&tmprect, QD(black));
  452.             }
  453.             if (percentresv > 0) {
  454.                 PenPat(QD(gray));
  455.                 MoveTo(progressrect.left + 1 + percentresv, progressrect.top);
  456.                 Line(0, 6);
  457.                 PenNormal();
  458.                 /* (or could draw a grayish area??) */
  459.             }
  460.         } else {
  461.             /* Draw a gray frame to indicate that this side has no units
  462.                that can actually do anything. */
  463.             PenPat(QD(gray));
  464.             FrameRect(&progressrect);
  465.             PenNormal();
  466.         }
  467.         /* (should this be a generic kernel test?) */
  468.         if (side2->finishedturn || !(side_has_ai(side2) || side_has_display(side2))) {
  469.             tmprect = progressrect;
  470.             InsetRect(&tmprect, 1, 1);
  471.             gray_out_rect(&tmprect);
  472.         }
  473.     }
  474.     if (keeping_score()) {
  475.         int i = 0;
  476.  
  477.         siderect.top += 24;
  478.         EraseRect(&siderect);
  479.         for_all_scorekeepers(sk) {
  480.             if (sk->title != NULL) {
  481.                 sprintf(spbuf, "%s", sk->title);
  482.             } else {
  483.                 sprintf(spbuf, "SK #%d", sk->id);
  484.             }
  485.             if (sk->scorenum >= 0) {
  486.                 tprintf(spbuf, ": %d", side2->scores[sk->scorenum]);
  487.             }
  488.             MoveTo(10 + (((i & 1) > 0) ? gamewinw/2 : 0) , siderect.top + 10 + 15 * (i/2));
  489.             DrawText(spbuf, 0, strlen(spbuf));
  490.             ++i;
  491.         }
  492.     }
  493. }
  494.  
  495. do_mouse_down_game()
  496. {
  497. }
  498.  
  499. /* The construction planning window. */
  500.  
  501. create_construction_window()
  502. {
  503.     int done = FALSE, u, num, wid, hgt;
  504.     short ditem, i;
  505.     Unit *unit;
  506.     Point pnt, cellsize;
  507.     Cell tmpcell;
  508.     Rect listrect, destrect, viewrect, tmprect;
  509.     short itemtype;  Handle itemhandle;  Rect itemrect;
  510.  
  511.     if (hasColorQD) {
  512.         constructionwin = GetNewCWindow(wConstruction, NULL, (WindowPtr) -1L);
  513.     } else {
  514.         constructionwin = GetNewWindow(wConstruction, NULL, (WindowPtr) -1L);
  515.     }
  516.     constructbutton = GetNewControl(cConstructButton, constructionwin);
  517.     researchbutton = GetNewControl(cResearchButton, constructionwin);
  518.     SetPort(constructionwin);
  519.     TextFont(monaco);
  520.     TextSize(9);
  521.     /* Set up the list of all constructing units. */
  522.     calc_construction_rects();
  523.     tmprect = unitlistrect;
  524.     tmprect.right -= sbarwid;
  525.     SetRect(&listrect, 0, 0, 1, 0);
  526.     SetPt(&cellsize, 300, 12);
  527.     /* Create the list of units itself. */
  528.     constructionunitlist =
  529.         LNew(&tmprect, &listrect, cellsize, 128, constructionwin,
  530.              FALSE, FALSE, FALSE, TRUE);
  531.     /* Now set up the list of types. */
  532.     tmprect = typelistrect;
  533.     tmprect.right -= sbarwid;
  534.     SetRect(&listrect, 0, 0, 1, 0);
  535.     /* (should calc this from the desired font) */
  536.     SetPt(&cellsize, 300, 12);
  537.     constructiontypelist =
  538.         LNew(&tmprect, &listrect, cellsize, 128, constructionwin,
  539.              FALSE, FALSE, FALSE, TRUE);
  540.     init_construction_lists();
  541.     /* (should try to keep the window out of the way of the frontmost map?) */
  542.     ShowWindow(constructionwin);
  543. }
  544.  
  545. init_construction_lists()
  546. {
  547.     int u, tm, num, i;
  548.     Unit *unit;
  549.     Point cellsize;
  550.     Cell tmpcell, tmpcell2;
  551.     Rect listrect, destrect, viewrect;
  552.  
  553.     /* Update the list of units. */
  554.     LDoDraw(0, constructionunitlist);
  555.     LDelRow(0, 0, constructionunitlist);
  556.     SetPt(&tmpcell, 0, 0);
  557.     /* Create the vector of constructing units, at a reasonable initial size. */
  558.     if (possibleunits == NULL) {
  559.         possibleunits = make_unit_vector(max(50, numunits));
  560.     }
  561.     clear_unit_vector(possibleunits);
  562.     for_all_side_units(dside, unit) {
  563.         maybe_add_unit_to_construction_list(unit);
  564.     }
  565.     LDoDraw(1, constructionunitlist);
  566.     /* Update the list of types. */
  567.     LDoDraw(0, constructiontypelist);
  568.     LDelRow(0, 0, constructiontypelist);
  569.     SetPt(&tmpcell, 0, 0);
  570.     if (possibletypes == NULL) {
  571.         possibletypes = (int *) xmalloc(numutypes * sizeof(int));
  572.     }
  573.     numposstypes = 0;
  574.     for_all_unit_types(u) {
  575.         if (1 /* could be built by some unit that could be on the side */) {
  576.             LAddRow(1, tmpcell.v, constructiontypelist);
  577.             constructible_desc(spbuf, dside, u, NULL);
  578.             LSetCell(spbuf, strlen(spbuf), tmpcell, constructiontypelist);
  579.             ++tmpcell.v;
  580.             possibletypes[numposstypes++] = u;
  581.         }
  582.     }
  583.     LDoDraw(1, constructiontypelist);
  584.     adjust_construction_controls();
  585. }
  586.  
  587. reinit_construction_lists()
  588. {
  589.     init_construction_lists();
  590. }
  591.  
  592. /* Draw the construction window by updating the lists and framing them. */
  593.  
  594. draw_construction()
  595. {
  596.     int u;
  597.     Point tmpcell;
  598.     Rect tmprect;
  599.  
  600.     calc_construction_rects();
  601.     LUpdate(constructionwin->visRgn, constructionunitlist);
  602.     tmprect = unitlistrect;
  603.     InsetRect(&tmprect, -1, -1);
  604.     FrameRect(&tmprect);
  605. #if 0
  606.     for_all_unit_types(u) {
  607.         if (1 /* could be built by some unit that could be on the side */) {
  608.             constructible_desc(spbuf, dside, u, possibleunits);
  609.             SetPt(&tmpcell, 0, u);
  610.             LSetCell(spbuf, strlen(spbuf), tmpcell, constructiontypelist);
  611.         }
  612.     }
  613. #endif
  614.     LUpdate(constructionwin->visRgn, constructiontypelist);
  615.     tmprect = typelistrect;
  616.     InsetRect(&tmprect, -1, -1);
  617.     FrameRect(&tmprect);
  618.     /* Maybe show the construct button as the default. */
  619.     draw_construction_default();
  620. }
  621.  
  622. /* Draw a heavy outline around the construction button. */
  623.  
  624. draw_construction_default()
  625. {
  626.     Rect tmprect;
  627.     GrafPtr oldport;
  628.  
  629.     GetPort(&oldport);
  630.     SetPort(constructionwin);
  631.     tmprect = (*constructbutton)->contrlRect;
  632.     PenSize(3, 3);
  633.     InsetRect(&tmprect, -4, -4);
  634.     if ((*constructbutton)->contrlHilite != 0) {
  635.         PenMode(patBic);
  636.     }
  637.     FrameRoundRect(&tmprect, 16, 16);
  638.     PenNormal();
  639.     SetPort(oldport);
  640. }
  641.  
  642. /* Figure out how to subdivide the construction window for the two lists. */
  643.  
  644. calc_construction_rects()
  645. {
  646.     int wid, hgt, divide;
  647.     Rect tmprect;
  648.  
  649.     tmprect = constructionwin->portRect;
  650.     wid = tmprect.right - tmprect.left - sbarwid;
  651.     hgt = tmprect.bottom - tmprect.top - sbarwid;
  652.     if (wid / 2 > 220 /* maxtypewid */) {
  653.         divide = wid - 220;
  654.     } else {
  655.         divide = wid / 2;
  656.     }
  657.     SetRect(&unitlistrect, 0, constructtop, divide, hgt);
  658.     InsetRect(&unitlistrect, constructmargin, constructmargin);
  659.     SetRect(&typelistrect, divide, constructtop, wid, hgt);
  660.     InsetRect(&typelistrect, constructmargin, constructmargin);
  661. }
  662.  
  663. activate_construction(activate)
  664. int activate;
  665. {
  666.     LActivate(activate, constructionunitlist);
  667.     LActivate(activate, constructiontypelist);
  668. }
  669.  
  670. Unit *
  671. get_selected_construction_unit()
  672. {
  673.     Point tmpcell;
  674.     Unit *unit;
  675.  
  676.     SetPt(&tmpcell, 0, 0);
  677.     if (LGetSelect(TRUE, &tmpcell, constructionunitlist)) {                
  678.         if (tmpcell.v < possibleunits->numunits) {
  679.             unit = (possibleunits->units)[tmpcell.v].unit;
  680.             if (is_acting(unit)) return unit;
  681.         }
  682.     }
  683.     return NULL;
  684. }
  685.  
  686. get_selected_construction_type()
  687. {
  688.     int u;
  689.     Point tmpcell;
  690.  
  691.     SetPt(&tmpcell, 0, 0);
  692.     if (LGetSelect(TRUE, &tmpcell, constructiontypelist)) {                
  693.         if (tmpcell.v < numposstypes) {
  694.             return possibletypes[tmpcell.v];
  695.         }
  696.     }
  697.     return NONUTYPE;
  698. }
  699.  
  700. do_mouse_down_construction(mouse, mods)
  701. Point mouse;
  702. int mods;
  703. {
  704.     ControlHandle control;
  705.     short part, value;
  706.     int u;
  707.     Point pt;
  708.     Unit *unit;
  709.  
  710.     part = FindControl(mouse, constructionwin, &control);
  711.     if (control == constructbutton) {
  712.         if ((unit = get_selected_construction_unit()) != NULL) {
  713.             if ((u = get_selected_construction_type()) != NONUTYPE) {
  714.                 push_build_task(unit, u, currunlength);
  715.                 update_construction_unit_list(unit);
  716.                 return;
  717.             }
  718.         }
  719.     } else if (control == researchbutton) {
  720.         if ((unit = get_selected_construction_unit()) != NULL) {
  721.             if ((u = get_selected_construction_type()) != NONUTYPE) {
  722.                 push_research_task(unit, u, u_tech_to_build(u));
  723.                 update_construction_unit_list(unit);
  724.                 return;
  725.             }
  726.         }
  727.     } else if (PtInRect(mouse, &unitlistrect)) {
  728.         LClick(mouse, mods, constructionunitlist);
  729.         /* Update the type list to show what could be built and in how long. */
  730.         update_type_list_for_unit(get_selected_construction_unit());
  731.     } else if (PtInRect(mouse, &typelistrect)) {
  732.         LClick(mouse, mods, constructiontypelist);
  733.         /* Update the unit list to show what could build the type */
  734.         update_unit_list_for_type(get_selected_construction_type());
  735.     } else {
  736.         /* Click was not in any useful part of the window. */ 
  737.     }
  738. }
  739.  
  740. /* Highlight exactly one specific unit in the construction window, and unhighlight
  741.    any others. */
  742.  
  743. select_unit_in_construction_window(unit)
  744. Unit *unit;
  745. {
  746.     int i;
  747.     Point tmpcell;
  748.  
  749.     for (i = 0; i < possibleunits->numunits; ++i) {
  750.         SetPt(&tmpcell, 0, i);
  751.         LSetSelect((unit == (possibleunits->units)[i].unit), tmpcell, constructionunitlist);
  752.         LAutoScroll(constructionunitlist);
  753.     }
  754.     update_type_list_for_unit(get_selected_construction_unit());
  755. }
  756.  
  757. select_type_in_construction_window(u)
  758. int u;
  759. {
  760.     int i;
  761.     Point tmpcell;
  762.  
  763.     for (i = 0; i < numutypes; ++i) {
  764.         SetPt(&tmpcell, 0, i);
  765.         LSetSelect((u == i), tmpcell, constructiontypelist);
  766.         LAutoScroll(constructiontypelist);
  767.     }
  768.     update_unit_list_for_type(get_selected_construction_type());
  769. }
  770.  
  771. /* Given a unit (which may be any unit), update the list of constructing units. */
  772.  
  773. update_construction_unit_list(unit)
  774. Unit *unit;
  775. {
  776.     int i, u;
  777.     Point tmpcell;
  778.  
  779.     if (constructionwin == nil) return;
  780.     u = get_selected_construction_type();
  781.     /* We need to look for it even if it might not be ours, since it might
  782.        have been captured or otherwise lost, and needs to be removed. */
  783.     for (i = 0; i < possibleunits->numunits; ++i) {
  784.         if (unit == (possibleunits->units)[i].unit) {
  785.             SetPt(&tmpcell, 0, i);
  786.             if (is_active(unit)
  787.                 && can_build(unit)
  788.                 && side_controls_unit(dside, unit)) {
  789.                 construction_desc(spbuf, unit, u);
  790.                 LSetCell(spbuf, strlen(spbuf), tmpcell, constructionunitlist);
  791.             } else {
  792.                 remove_unit_from_vector(possibleunits, unit, i);
  793.                 LDelRow(1, tmpcell.v, constructionunitlist);
  794.             }
  795.             return;
  796.         }
  797.     }
  798.     /* Unit was not found, try to add it to the list. */
  799.     maybe_add_unit_to_construction_list(unit);
  800. }
  801.  
  802. maybe_add_unit_to_construction_list(unit)
  803. Unit *unit;
  804. {
  805.     Point tmpcell;
  806.  
  807.     if (is_acting(unit)
  808.         && can_build(unit)
  809.         && side_controls_unit(dside, unit)) {
  810.         /* Add this unit to the vector of constructing units. */
  811.         possibleunits = add_unit_to_vector(possibleunits, unit, 0);
  812.         /* (should sort and maybe rearrange list here) */
  813.         /* Add a row at the end of the list. */
  814.         SetPt(&tmpcell, 0, possibleunits->numunits - 1);
  815.         LAddRow(1, possibleunits->numunits - 1, constructionunitlist);
  816.         construction_desc(spbuf, unit, get_selected_construction_type());
  817.         LSetCell(spbuf, strlen(spbuf), tmpcell, constructionunitlist);
  818.     }
  819. }
  820.  
  821. update_unit_list_for_type(u)
  822. int u;
  823. {
  824.     int i;
  825.     Point tmpcell;
  826.     Unit *unit;
  827.  
  828.     for (i = 0; i < possibleunits->numunits; ++i) {
  829.         if ((unit = (possibleunits->units)[i].unit) != NULL) {
  830.             SetPt(&tmpcell, 0, i);
  831.             if (is_acting(unit) && unit->side == dside) {
  832.                 construction_desc(spbuf, unit, u);
  833.                 LSetCell(spbuf, strlen(spbuf), tmpcell, constructionunitlist);
  834.             } else {
  835. /*                LDelRow(1, tmpcell.v, constructionunitlist); */
  836.                 LSetCell("", 0, tmpcell, constructionunitlist);
  837.             }
  838.         }
  839.     }
  840.     adjust_construction_controls();
  841. }
  842.  
  843. update_construction_type_list()
  844. {
  845.     int i, u;
  846.     Point tmpcell;
  847.  
  848.     if (constructionwin == nil) return;
  849.     u = get_selected_construction_type();
  850.     update_type_list_for_unit(get_selected_construction_unit());
  851. }
  852.  
  853. update_type_list_for_unit(unit)
  854. Unit *unit;
  855. {
  856.     int i, u;
  857.     Point tmpcell;
  858.  
  859.     for_all_unit_types(u) {
  860.         if (1 /* could be built by some unit that could be on the side */) {
  861.             SetPt(&tmpcell, 0, u);
  862.             constructible_desc(spbuf, dside, u, get_selected_construction_unit());
  863.             LSetCell(spbuf, strlen(spbuf), tmpcell, constructiontypelist);
  864.         }
  865.     }
  866.     adjust_construction_controls();
  867. }
  868.  
  869. /* Enable/disable controls according to whether the selected list elements can
  870.    do construction activities. */
  871.  
  872. adjust_construction_controls()
  873. {
  874.     int u, canconstruct = FALSE, canresearch = FALSE;
  875.     Unit *unit;
  876.  
  877.     if ((unit = get_selected_construction_unit()) != NULL) {
  878.         if ((u = get_selected_construction_type()) != NONUTYPE) {
  879.             if (uu_acp_to_create(unit->type, u) > 0) canconstruct = TRUE;
  880.             if (uu_acp_to_research(unit->type, u) > 0) canresearch = TRUE;
  881.         }
  882.     }
  883.     HiliteControl(constructbutton, (canconstruct ? 0 : 255));
  884.     HiliteControl(researchbutton, (canresearch ? 0 : 255));
  885.     draw_construction_default();
  886. }
  887.  
  888. /* Resize the construction window to the given size. */
  889.  
  890. grow_construction(h, v)
  891. short h, v;
  892. {
  893.     int wid, hgt, leftwid;
  894.     Rect tmprect;
  895.  
  896.     EraseRect(&constructionwin->portRect);
  897.     SizeWindow(constructionwin, h, v, 1);
  898.     /* Recalculate size and position of the unit and type lists. */
  899.     calc_construction_rects();
  900.     LSize(unitlistrect.right - unitlistrect.left - sbarwid,
  901.           unitlistrect.bottom - unitlistrect.top,
  902.           constructionunitlist);
  903.     /* Move the type list (is this the approved way to do it?) */
  904.     (*constructiontypelist)->rView.left = typelistrect.left;
  905.     LSize(typelistrect.right - typelistrect.left - sbarwid,
  906.           typelistrect.bottom - typelistrect.top,
  907.           constructiontypelist);
  908.     /* This will force a full redraw at the next update. */
  909.     InvalRect(&constructionwin->portRect);
  910. }                    
  911.  
  912. /* Zooming "rightsizes" the window. */
  913.  
  914. zoom_construction()
  915. {
  916. }
  917.  
  918. /* The side closeup window. */
  919.  
  920. create_side_closeup(side2)
  921. Side *side2;
  922. {
  923.     SideCloseup *sidecloseup = (SideCloseup *) xmalloc(sizeof(SideCloseup));
  924.     Rect vscrollrect, hscrollrect;
  925.  
  926.     DGprintf("Creating a side closeup\n");
  927.     sidecloseup->side = side2;
  928.     sidecloseup->dialog = GetNewDialog(dSideCloseup, nil, (DialogPtr) -1L);
  929.     sidecloseup->next = sidecloseuplist;
  930.     sidecloseuplist = sidecloseup;
  931.     ShowWindow(sidecloseup->dialog);
  932.     add_window_menu_item("Side Closeup", sidecloseup->dialog);
  933. }
  934.  
  935.  
  936. SideCloseup *
  937. side_closeup_from_window(window)
  938. WindowPtr window;
  939. {
  940.     SideCloseup *sidecloseup;
  941.     
  942.     for_all_side_closeups(sidecloseup) {
  943.         if (sidecloseup->dialog == window) return sidecloseup;
  944.     }
  945.     return NULL;
  946. }
  947.  
  948. draw_side_closeup(sidecloseup)
  949. SideCloseup *sidecloseup;
  950. {
  951.     if (!active_display(dside) || sidecloseup == NULL) return;
  952.     DrawDialog(sidecloseup->dialog);
  953. }
  954.  
  955. hit_in_side_closeup(dialog, itemhit, evt)
  956. DialogPtr dialog;
  957. short itemhit;
  958. EventRecord *evt;
  959. {
  960.     int closewhendone = FALSE;
  961.     SideCloseup *sidecloseup = side_closeup_from_window(dialog);
  962.     short itemtype;  Handle itemhandle;  Rect itemrect;
  963.     
  964.     if (sidecloseup == NULL) return FALSE;
  965.  
  966.     switch (itemhit) {
  967.         case diSideCloseupMachineStrategy:
  968.             GetDItem(dialog, diSideCloseupMachineStrategy, &itemtype, &itemhandle, &itemrect);
  969.             SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
  970.             break;
  971.         case diSideCloseupAutofinish:
  972.             GetDItem(dialog, diSideCloseupAutofinish, &itemtype, &itemhandle, &itemrect);
  973.             SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
  974.             break;
  975.         case diSideCloseupOK:
  976.             closewhendone = TRUE;
  977.         case diSideCloseupApply:
  978.             /* apply all the bits to the side. */
  979.             GetDItem(dialog, diSideCloseupMachineStrategy, &itemtype, &itemhandle, &itemrect);
  980.             dside->usemachinestrategy = GetCtlValue((ControlHandle) itemhandle);
  981.             GetDItem(dialog, diSideCloseupAutofinish, &itemtype, &itemhandle, &itemrect);
  982.             dside->autofinish = GetCtlValue((ControlHandle) itemhandle);
  983.             break;
  984.         case diSideCloseupCancel:
  985.             closewhendone = TRUE;
  986.             break;
  987.     }
  988.     if (closewhendone) {
  989.         HideWindow(sidecloseup->dialog);
  990.     }
  991.     return TRUE;
  992. }
  993.  
  994. char *
  995. get_string_from_item(itemhandle)
  996. Handle itemhandle;
  997. {
  998.     char tmpbuf[BUFSIZE];
  999.     Str255 tmpstr;
  1000.     
  1001.     GetIText(itemhandle, tmpstr);
  1002.     /* This is basically p2c. */
  1003.      strncpy(tmpbuf, tmpstr+1, tmpstr[0]);
  1004.     /* Make sure the string is terminated properly. */
  1005.     tmpbuf[tmpstr[0]] = '\0';
  1006.     return copy_string(tmpbuf);
  1007. }
  1008.  
  1009. side_rename_dialog(side)
  1010. Side *side;
  1011. {
  1012.     short done = FALSE, changed = TRUE, ditem;
  1013.     Str255 tmpstr;
  1014.     DialogPtr win;
  1015.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1016.  
  1017.     win = GetNewDialog(dSideRename, NULL, (DialogPtr) -1L);
  1018.     while (!done) {
  1019.         if (changed) {
  1020.             /* Seed the items with the current side names. */
  1021.             GetDItem(win, diSideRenameName, &itemtype, &itemhandle, &itemrect);
  1022.             c2p((side->name ? side->name : ""), tmpstr);
  1023.             SetIText(itemhandle, tmpstr);
  1024.             GetDItem(win, diSideRenameFullName, &itemtype, &itemhandle, &itemrect);
  1025.             c2p((side->longname ? side->longname : ""), tmpstr);
  1026.             SetIText(itemhandle, tmpstr);
  1027.             GetDItem(win, diSideRenameAcronym, &itemtype, &itemhandle, &itemrect);
  1028.             c2p((side->shortname ? side->shortname : ""), tmpstr);
  1029.             SetIText(itemhandle, tmpstr);
  1030.             GetDItem(win, diSideRenameNoun, &itemtype, &itemhandle, &itemrect);
  1031.             c2p((side->noun ? side->noun : ""), tmpstr);
  1032.             SetIText(itemhandle, tmpstr);
  1033.             GetDItem(win, diSideRenamePluralNoun, &itemtype, &itemhandle, &itemrect);
  1034.             c2p((side->pluralnoun ? side->pluralnoun : ""), tmpstr);
  1035.             SetIText(itemhandle, tmpstr);
  1036.             GetDItem(win, diSideRenameAdjective, &itemtype, &itemhandle, &itemrect);
  1037.             c2p((side->adjective ? side->adjective : ""), tmpstr);
  1038.             SetIText(itemhandle, tmpstr);
  1039.             GetDItem(win, diSideRenameEmblemName, &itemtype, &itemhandle, &itemrect);
  1040.             c2p((side->emblemname ? side->emblemname : ""), tmpstr);
  1041.             SetIText(itemhandle, tmpstr);
  1042.             GetDItem(win, diSideRenameColorScheme, &itemtype, &itemhandle, &itemrect);
  1043.             c2p((side->colorscheme ? side->colorscheme : ""), tmpstr);
  1044.             SetIText(itemhandle, tmpstr);
  1045.             ShowWindow(win);
  1046.             changed = FALSE;
  1047.         }
  1048.         draw_default_button(win, diSideRenameOK);
  1049.         ModalDialog(NULL, &ditem);
  1050.         switch (ditem) {
  1051.             case diSideRenameOK:
  1052.                 /* Actually change the side's slots. */
  1053.                 GetDItem(win, diSideRenameName, &itemtype, &itemhandle, &itemrect);
  1054.                 set_side_name(dside, dside, get_string_from_item(itemhandle));
  1055.                 GetDItem(win, diSideRenameFullName, &itemtype, &itemhandle, &itemrect);
  1056.                 side->longname = get_string_from_item(itemhandle);
  1057.                 GetDItem(win, diSideRenameAcronym, &itemtype, &itemhandle, &itemrect);
  1058.                 side->shortname = get_string_from_item(itemhandle);
  1059.                 GetDItem(win, diSideRenameNoun, &itemtype, &itemhandle, &itemrect);
  1060.                 side->noun = get_string_from_item(itemhandle);
  1061.                 GetDItem(win, diSideRenamePluralNoun, &itemtype, &itemhandle, &itemrect);
  1062.                 side->pluralnoun = get_string_from_item(itemhandle);
  1063.                 GetDItem(win, diSideRenameAdjective, &itemtype, &itemhandle, &itemrect);
  1064.                 side->adjective = get_string_from_item(itemhandle);
  1065.                 GetDItem(win, diSideRenameEmblemName, &itemtype, &itemhandle, &itemrect);
  1066.                 side->emblemname = get_string_from_item(itemhandle);
  1067.                 GetDItem(win, diSideRenameColorScheme, &itemtype, &itemhandle, &itemrect);
  1068.                 side->colorscheme = get_string_from_item(itemhandle);
  1069.                 /* Tweak the side menu. */
  1070.                 update_side_menu(dside);
  1071.                 /* Force redisplay of everything that might use any side names. */
  1072.                 force_overall_update();
  1073.                 /* Fall into next case. */
  1074.             case diSideRenameCancel:
  1075.                 done = TRUE;
  1076.                 break;
  1077.             case diSideRenameRandom:
  1078.                 side->name = NULL;
  1079.                 side->noun = NULL;
  1080.                 /* always need to clear this cache before renaming... */
  1081.                 side->pluralnoun = NULL;
  1082.                 side->adjective = NULL;
  1083.                 make_up_side_name(side);
  1084.                 init_emblem_images(side);
  1085.                 changed = TRUE;
  1086.                 break;
  1087.         }
  1088.     }
  1089.     DisposDialog(win);
  1090. }
  1091.  
  1092. /* Generic renaming, applies to both units and features. */
  1093.  
  1094. unit_rename_dialog(unit)
  1095. Unit *unit;
  1096. {
  1097.     short done = FALSE, ditem;
  1098.     char *newname;
  1099.     char *namer = unit_namer(unit);
  1100.     Str255 tmpstr;
  1101.     DialogPtr win;
  1102.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1103.  
  1104.     if (unit == NULL) return;
  1105.     win = GetNewDialog(dRename, NULL, (DialogPtr) -1L);
  1106.     /* Seed the text item with the original name. */
  1107.     newname = unit->name;
  1108.     if (newname == NULL) newname = "";
  1109.     GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
  1110.     c2p(newname, tmpstr);
  1111.     SetIText(itemhandle, tmpstr);
  1112.     /* Gray out the random renaming button if no namers available. */
  1113.     GetDItem(win, diRenameRandom, &itemtype, &itemhandle, &itemrect);
  1114.     HiliteControl((ControlHandle) itemhandle, ((namer != NULL) ? 0 : 255));
  1115.     ShowWindow(win);
  1116.     while (!done) {
  1117.         draw_default_button(win, diRenameOK);
  1118.         ModalDialog(NULL, &ditem);
  1119.         switch (ditem) {
  1120.             case diRenameOK:
  1121.                 GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
  1122.                 set_unit_name(dside, unit, get_string_from_item(itemhandle));
  1123.                 /* Fall into next case. */
  1124.             case diRenameCancel:
  1125.                 done = TRUE;
  1126.                 break;
  1127.             case diRenameRandom:
  1128.                 newname = propose_unit_name(unit);
  1129.                 GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
  1130.                 c2p(newname, tmpstr);
  1131.                 SetIText(itemhandle, tmpstr);
  1132.                 break;
  1133.         }
  1134.     }
  1135.     DisposDialog(win);
  1136. }
  1137.  
  1138. /* Unit closeups. */
  1139.  
  1140. UnitCloseup *
  1141. find_unit_closeup(unit)
  1142. Unit *unit;
  1143. {
  1144.     UnitCloseup *unitcloseup;
  1145.  
  1146.     for_all_unit_closeups(unitcloseup) {
  1147.         if (unitcloseup->unit == unit
  1148.             && unitcloseup->window
  1149.             && ((WindowPeek) unitcloseup->window)->visible)
  1150.           return unitcloseup;
  1151.     }
  1152.     return NULL;
  1153. }
  1154.  
  1155. create_unit_closeup(unit)
  1156. Unit *unit;
  1157. {
  1158.     int u, w, h, v;
  1159.     Str255 tmpstr;
  1160.     WindowPtr win;
  1161.     UnitCloseup *unitcloseup = (UnitCloseup *) xmalloc(sizeof(UnitCloseup));
  1162.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1163.  
  1164.     if (!active_display(dside) || unit == NULL) return;
  1165.     DGprintf("Creating a closeup of %s\n", unit_desig(unit));
  1166.     u = unit->type;
  1167.     unitcloseup->unit = unit;
  1168.     if (hasColorQD) {
  1169.         win = GetNewCWindow(wUnitCloseup, nil, (WindowPtr) -1L);
  1170.     } else {
  1171.         win = GetNewWindow(wUnitCloseup, nil, (WindowPtr) -1L);
  1172.     }
  1173.     unitcloseup->window = win;
  1174.     stagger_window(unitcloseup->window, &lastunitcloseuph, &lastunitcloseupv);
  1175.     preferred_closeup_size(u, &w, &h);
  1176.     SizeWindow(win, w, h, 1);
  1177.     sprintf(spbuf, "%s #%d Closeup", u_type_name(u), unit->id);
  1178.     add_window_menu_item(spbuf, win);
  1179.     unitcloseup->next = unitcloseuplist;
  1180.     unitcloseuplist = unitcloseup;
  1181.     /* We're now ready to show this closeup to the world. */
  1182.     ShowWindow(win);
  1183. }
  1184.  
  1185. preferred_closeup_size(u, widp, hgtp)
  1186. int u, *widp, *hgtp;
  1187. {
  1188.     int wid = 200, hgt = 100;
  1189.  
  1190.     /* (should base on only the mtypes that the unit can hold) */
  1191.     if (nummtypes > 0) hgt += nummtypes * 20;
  1192.     if (u_acp(u) > 0) hgt += 120;
  1193.     *widp = wid;  *hgtp = hgt;
  1194. }
  1195.  
  1196. draw_unit_closeup(unitcloseup)
  1197. UnitCloseup *unitcloseup;
  1198. {
  1199.     int u, m, sx = 4, sy;
  1200.     char tmpbuf[BUFSIZE];
  1201.     Str255 tmpstr;
  1202.     Rect tmprect;
  1203.     GrafPtr oldport;
  1204.     WindowPtr win = unitcloseup->window;
  1205.     Unit *unit = unitcloseup->unit;
  1206.  
  1207.     if (!active_display(dside)) return;
  1208.     if (!in_play(unit) || !side_controls_unit(dside, unit)) {
  1209.         /* If the unit is no longer alive and ours, shut down the window. */
  1210.         remove_window_menu_item(win);
  1211.         destroy_unit_closeup(unitcloseup);
  1212.         HideWindow(win);
  1213.         return;
  1214.     }
  1215.     GetPort(&oldport);
  1216.     SetPort(win);
  1217.     EraseRect(&win->portRect);
  1218.     u = unit->type;
  1219.     /* Draw the unit's image. */
  1220.     SetRect(&tmprect, sx, sx, sx + 32, sx + 32); 
  1221.     EraseRect(&tmprect);
  1222.     draw_unit_image(win, tmprect.left, tmprect.top,
  1223.                     tmprect.right - tmprect.left, tmprect.bottom - tmprect.top,
  1224.                     u, side_number(unit->side), !completed(unit));
  1225.     /* Draw the unit's name. */
  1226.     name_or_number(unit, tmpbuf);
  1227.     MoveTo(45, 21);
  1228.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1229.     if (Debug || DebugG || DebugM) {
  1230.         sprintf(tmpbuf, " %d ", unit->id);
  1231.         MoveTo(170, 15);
  1232.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1233.     }
  1234.     side_and_type_name(tmpbuf, dside, u, unit->side);
  1235.     MoveTo(45, 41);
  1236.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1237.     if (unit->z != 0) {
  1238.         sprintf(tmpbuf, "; alt %d", unit->z);
  1239.     } else {
  1240.         strcpy(tmpbuf, "");
  1241.     }
  1242.     sprintf(spbuf, "at %d,%d%s", unit->x, unit->y, tmpbuf);
  1243.     if (unit->transport != NULL) {
  1244.         sprintf(tmpbuf, "In %s (%s)", short_unit_handle(unit->transport), spbuf);
  1245.     } else {
  1246.         strcpy(tmpbuf, spbuf);
  1247.     }
  1248.     sy = 61;
  1249.     MoveTo(sx, sy);
  1250.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1251.     hp_desc(tmpbuf, unit);
  1252.     sy += 20;
  1253.     MoveTo(sx, sy);
  1254.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1255.     acp_desc(tmpbuf, unit);
  1256.     sy += 20;
  1257.     MoveTo(sx, sy);
  1258.     DrawText(tmpbuf, 0, strlen(tmpbuf));
  1259.     /* Display the unit's supplies. */
  1260.     for_all_material_types(m) {
  1261.         strcpy(tmpbuf, m_type_name(m));
  1262.         sy += 20;
  1263.         MoveTo(sx, sy);
  1264.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1265.         sprintf(tmpbuf, "%d", unit->supply[m]);
  1266.         MoveTo(sx + 80, sy);
  1267.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1268.     }
  1269.     if (unit->plan) {
  1270.         Task *task;
  1271.         extern char *plantypenames[];
  1272.         Plan *plan = unit->plan;
  1273.         
  1274.         sprintf(tmpbuf, "Plan: %s", plantypenames[plan->type]);
  1275.         if (plan->waitingfortasks) strcat(tmpbuf, " [waiting]");
  1276.         if (plan->reserve) strcat(tmpbuf, " [reserve]");
  1277.         if (plan->asleep) strcat(tmpbuf, " [asleep]");
  1278.         if (plan->aicontrol) strcat(tmpbuf, " [delegated]");
  1279.         sy += 20;
  1280.         MoveTo(sx, sy);
  1281.         DrawText(tmpbuf, 0, strlen(tmpbuf));
  1282.         if (plan->maingoal) {
  1283.             /* (should have a "goal_desc" routine) */
  1284.             strcpy(tmpbuf, goal_desig(plan->maingoal));
  1285.             sy += 20;            
  1286.             MoveTo(sx, sy);
  1287.             DrawText(tmpbuf, 0, strlen(tmpbuf));
  1288.         }
  1289.         if (plan->tasks) {
  1290.             for (task = plan->tasks; task != NULL; task = task->next) {
  1291.                 task_desc(tmpbuf, task);
  1292.                 sy += 20;            
  1293.                 MoveTo(sx, sy);
  1294.                 DrawText(tmpbuf, 0, strlen(tmpbuf));
  1295.             }
  1296.         }
  1297.     }
  1298.     SetPort(oldport);
  1299. }
  1300.  
  1301.  
  1302. UnitCloseup *
  1303. unit_closeup_from_window(win)
  1304. WindowPtr win;
  1305. {
  1306.     UnitCloseup *unitcloseup;
  1307.     
  1308.     for_all_unit_closeups(unitcloseup) {
  1309.         if (unitcloseup->window == win) return unitcloseup;
  1310.     }
  1311.     return NULL;
  1312. }
  1313.  
  1314. do_mouse_down_unit_closeup(unitcloseup, mouse, mods)
  1315. UnitCloseup *unitcloseup;
  1316. Point mouse;
  1317. int mods;
  1318. {
  1319.     ControlHandle control;
  1320.     short part, value;
  1321.     WindowPtr window = unitcloseup->window;
  1322.  
  1323.     part = FindControl(mouse, window, &control);
  1324.     if (0 /* some control */) {
  1325.     } else {
  1326.         /* This just forces a redraw of the window - kind of crude. */
  1327.         update_unit_display(dside, unitcloseup->unit, TRUE);
  1328.         return TRUE;
  1329.     }
  1330. }
  1331.  
  1332. destroy_unit_closeup(unitcloseup)
  1333. UnitCloseup *unitcloseup;
  1334. {
  1335.     UnitCloseup *unitcloseup2;
  1336.     
  1337.     if (unitcloseuplist == unitcloseup) {
  1338.         unitcloseuplist = unitcloseup->next;
  1339.     } else {
  1340.         for_all_unit_closeups(unitcloseup2) {
  1341.             if (unitcloseup2->next == unitcloseup) {
  1342.                 unitcloseup2->next = unitcloseup->next;
  1343.             }
  1344.         }
  1345.     }
  1346.     /* (should destroy substructs) */
  1347.     free(unitcloseup);
  1348. }
  1349.  
  1350. /* History window. */
  1351.  
  1352. HistEvent *
  1353. get_nth_history_line(n)
  1354. int n;
  1355. {
  1356.     int i = 1;
  1357.     HistEvent *hevt;
  1358.     
  1359.     for (hevt = history->next; hevt != history; hevt = hevt->next) {
  1360.         if (1 /* passes filter */) {
  1361.             if (i == n || (i - 1) == n) return hevt;
  1362.             if (hevt->startdate != hevt->prev->startdate) ++i;
  1363.             ++i;
  1364.         }
  1365.     }
  1366.     /* Return the last event. */
  1367.     return history->prev;
  1368. }
  1369.  
  1370. create_history_window()
  1371. {
  1372.     Rect vscrollrect;
  1373.  
  1374.     historywin = GetNewWindow(wHistory, NULL, (WindowPtr) -1L);
  1375.     histcontents = (HistEvent **) xmalloc(100 * sizeof(HistEvent *));
  1376.     vscrollrect = historywin->portRect;
  1377.     vscrollrect.top -= 1;
  1378.     vscrollrect.bottom -= sbarwid - 1;
  1379.     vscrollrect.left = vscrollrect.right - sbarwid;
  1380.     vscrollrect.right += 1;
  1381.     histvscrollbar = NewControl(historywin, &vscrollrect, "\p", TRUE,
  1382.                                  0, 0, 100, scrollBarProc, 0L);
  1383.     /* Start off with the most recent event.  This will be adjusted by scrollbar
  1384.        setting if the window is > 1 line long. */
  1385.     firstvisevt = history->prev;
  1386.     update_total_hist_lines();
  1387.     set_history_scrollbar();
  1388. }
  1389.  
  1390. calc_history_layout()
  1391. {
  1392.     update_total_hist_lines();
  1393.     set_history_scrollbar();
  1394. }
  1395.  
  1396. update_total_hist_lines()
  1397. {
  1398.     int i = 1;
  1399.     HistEvent *hevt;
  1400.     
  1401.     totalhistlines = 0;
  1402.     for (hevt = history->next; hevt != history; hevt = hevt->next) {
  1403.         if (1 /* passes filter */) {
  1404.             if (hevt->startdate != hevt->prev->startdate) ++totalhistlines;
  1405.             ++totalhistlines;
  1406.         }
  1407.     }
  1408. }
  1409.  
  1410. set_history_scrollbar()
  1411. {
  1412.     int hgt, i, oldmax;
  1413.  
  1414.     hgt = historywin->portRect.bottom - historywin->portRect.top;
  1415.     numvishistlines = (hgt - sbarwid) / histlinespacing;
  1416.     oldmax = GetCtlMax(histvscrollbar);
  1417.     SetCtlMax(histvscrollbar, max(0, totalhistlines - numvishistlines));
  1418.     HiliteControl(histvscrollbar, (numvishistlines < totalhistlines ? 0 : 255));
  1419.     if (GetCtlValue(histvscrollbar) == oldmax) {
  1420.         SetCtlValue(histvscrollbar, GetCtlMax(histvscrollbar));
  1421.         if (GetCtlValue(histvscrollbar) == 0) {
  1422.             firstvisevt = history->next;
  1423.         } else {
  1424.             firstvisevt = get_nth_history_line(GetCtlValue(histvscrollbar));
  1425.         }
  1426.     }
  1427. }
  1428.  
  1429. draw_history()
  1430. {
  1431.     int i;
  1432.     HistEvent *hevt;
  1433.  
  1434.     if (!active_display(dside) || historywin == nil) return;
  1435.  
  1436.     numhistcontents = 0;
  1437.     /* Always show a date on the first line of the window. */
  1438.     histcontents[numhistcontents++] = NULL;
  1439.     histcontents[numhistcontents++] = firstvisevt;
  1440.     for (hevt = firstvisevt->next; hevt != history; hevt = hevt->next) {
  1441.         if (1 /* passes filter */) {
  1442.             if (hevt->startdate != hevt->prev->startdate) {
  1443.                 histcontents[numhistcontents++] = NULL;
  1444.             }
  1445.             histcontents[numhistcontents++] = hevt;
  1446.             if (numhistcontents >= numvishistlines) break;
  1447.         }
  1448.     }
  1449.     for (i = 0; i < numvishistlines; ++i) {
  1450.         if (histcontents[i] != NULL) {
  1451.             draw_historical_event(histcontents[i], i);
  1452.         } else {
  1453.             draw_historical_date(histcontents[i+1], i);
  1454.         }
  1455.     }
  1456. }
  1457.  
  1458. draw_historical_event(hevt, y)
  1459. HistEvent *hevt;
  1460. int y;
  1461. {
  1462.     int numchars;
  1463.     char hevtbuf[500];
  1464.  
  1465.     if (hevt == NULL) return;
  1466.     MoveTo(20, histlinespacing * y + 10);
  1467.     historical_event_desc(hevt, hevtbuf);
  1468.     /* (should clip to drawing only visible chars) */
  1469.     numchars = strlen(hevtbuf);
  1470.     DrawText(hevtbuf, 0, numchars);
  1471. }
  1472.  
  1473. draw_historical_date(hevt, y)
  1474. HistEvent *hevt;
  1475. int y;
  1476. {
  1477.     int numchars;
  1478.     char *datestr, hdatebuf[100];
  1479.  
  1480.     if (hevt == NULL) return;
  1481.     MoveTo(2, histlinespacing * y + 10);
  1482.     /* (should be relative) */
  1483.     datestr = absolute_date_string(hevt->startdate);
  1484.     if (hevt->startdate == hevt->prev->startdate) {
  1485.         sprintf(hdatebuf, "(%s)", datestr);
  1486.     } else {
  1487.         strcpy(hdatebuf, datestr);
  1488.     }
  1489.     /* (should clip to drawing only visible chars) */
  1490.     numchars = strlen(hdatebuf);
  1491.     DrawText(hdatebuf, 0, numchars);
  1492. }
  1493.  
  1494. pascal void
  1495. history_scroll_proc(control, code)
  1496. ControlHandle control;
  1497. short code;
  1498. {
  1499.     int curvalue, maxvalue, pagesize, jump;
  1500.  
  1501.     curvalue = GetCtlValue(control);
  1502.     maxvalue = GetCtlMax(control);
  1503.     pagesize = numvishistlines;
  1504.     switch (code) {
  1505.         case inPageDown:
  1506.             jump = pagesize - 1;
  1507.             break;
  1508.         case inDownButton:
  1509.             jump = 1;
  1510.             break;
  1511.         case inPageUp:
  1512.             jump = - (pagesize - 1);
  1513.             break;
  1514.         case inUpButton:
  1515.             jump = -1;
  1516.             break;
  1517.         default:
  1518.             jump = 0;
  1519.             break;
  1520.     }
  1521.     curvalue = max(min(curvalue + jump, maxvalue), 0);
  1522.     SetCtlValue(control, curvalue);
  1523. }
  1524.  
  1525. do_mouse_down_history(mouse, mods)
  1526. Point mouse;
  1527. int mods;
  1528. {
  1529.     ControlHandle control;
  1530.     short part, value;
  1531.  
  1532.     part = FindControl(mouse, historywin, &control);
  1533.     if (control == histvscrollbar) {
  1534.         switch (part) {
  1535.             case inThumb:
  1536.                 part = TrackControl(control, mouse, NULL);
  1537.                 firstvisevt = get_nth_history_line(GetCtlValue(control));
  1538.                 force_update(historywin);
  1539.                 break;
  1540.             default:
  1541.                 part = TrackControl(control, mouse, (ProcPtr) history_scroll_proc);
  1542.                 firstvisevt = get_nth_history_line(GetCtlValue(control));
  1543.                 force_update(historywin);
  1544.                 break;
  1545.         }
  1546.     } else {
  1547.         /* anything to do here? */
  1548.     }
  1549. }
  1550.  
  1551. /* Grow/shrink the history window to the given size. */
  1552.  
  1553. grow_history(h, v)
  1554. short h, v;
  1555. {
  1556.     Rect tmprect;
  1557.  
  1558.     EraseRect(&historywin->portRect);
  1559.     SizeWindow(historywin, h, v, 1);
  1560.     MoveControl(histvscrollbar, h - sbarwid, 0);
  1561.     SizeControl(histvscrollbar, sbarwid + 1, v - sbarwid + 1);
  1562.     set_history_scrollbar();
  1563.     /* This will force a full redraw at the next update. */
  1564.     InvalRect(&historywin->portRect);
  1565. }                    
  1566.  
  1567. zoom_history(part)
  1568. int part;
  1569. {
  1570.     int titleh, h, v, vislinesavail;
  1571.     Rect winrect, zoomrect, gdrect;
  1572.     GDHandle gd, zoomgd;
  1573.  
  1574.     EraseRect(&historywin->portRect);
  1575.     if (part == inZoomOut) {
  1576.         if (!hasColorQD) {
  1577.             /* If no Color QD, then there is only one screen. */
  1578.             zoomrect = screenBits.bounds;
  1579.             InsetRect(&zoomrect, 4, 4);
  1580.         } else {
  1581.             for (gd = GetDeviceList(); gd != nil; gd = GetNextDevice(gd)) {
  1582.                 if (TestDeviceAttribute(gd, screenDevice)) {
  1583.                     if (TestDeviceAttribute(gd, screenActive)) {
  1584.                         zoomgd = gd;
  1585.                         break;
  1586.                     }
  1587.                 }
  1588.             }
  1589.             zoomrect = (*zoomgd)->gdRect;
  1590.             if (zoomgd == GetMainDevice()) {
  1591.                 zoomrect.top += GetMBarHeight();
  1592.             }
  1593.             titleh = 20; /* (should calc) */
  1594.             zoomrect.top += titleh;
  1595.             InsetRect(&zoomrect, 3, 3);
  1596.             vislinesavail = (zoomrect.bottom - zoomrect.top - sbarwid) / histlinespacing;
  1597.             if (vislinesavail > numhistcontents) {
  1598.                 zoomrect.bottom = zoomrect.top + numhistcontents * histlinespacing + sbarwid;
  1599.             }
  1600.         }
  1601.         (*((WStateDataHandle) ((WindowPeek) historywin)->dataHandle))->stdState = zoomrect;
  1602.     }
  1603.     ZoomWindow(historywin, part, (historywin == FrontWindow()));
  1604.     h = window_width(historywin);  v = window_height(historywin);
  1605.     MoveControl(histvscrollbar, h - sbarwid, 0);
  1606.     SizeControl(histvscrollbar, sbarwid + 1, v - sbarwid + 1);
  1607.     set_history_scrollbar();
  1608.     /* This will force a full redraw at the next update. */
  1609.     InvalRect(&historywin->portRect);
  1610. }
  1611.  
  1612. /* Help window. */
  1613.  
  1614. /* This is the top-level access to bring up the help window, can be called
  1615.    anywhere, anytime. */
  1616.  
  1617. help_dialog()
  1618. {
  1619.     if (helpwin == nil) {
  1620.         create_help_window();
  1621.     }
  1622.     ShowWindow(helpwin);
  1623.     SelectWindow(helpwin);
  1624. }
  1625.  
  1626. describe_menus(arg, key, buf)
  1627. int arg;
  1628. char *key, *buf;
  1629. {
  1630.     strcat(buf, "File\n");
  1631.     strcat(buf, "  The usual file operations.\n");
  1632.     strcat(buf, "\n");
  1633.     strcat(buf, "Edit\n");
  1634.     strcat(buf, "  Select All selects all of your units at once.\n");
  1635.     strcat(buf, "\n");
  1636.     strcat(buf, "Find\n");
  1637.     strcat(buf, "  Find Selected goes to the next map back and looks at the unit selected or viewed in the front window.\n");
  1638.     strcat(buf, "\n");
  1639.     strcat(buf, "Play\n");
  1640.     strcat(buf, "  This is the main menu for unit actions.");
  1641.     strcat(buf, "  If an action is grayed out, then none of the selected units can do it.\n");
  1642.     strcat(buf, "  Closeup shows details of the selected units.\n");
  1643.     strcat(buf, "  Build brings up the construction dialog.\n");
  1644.     strcat(buf, "Side\n");
  1645.     strcat(buf, "  This menu is for things that affect your whole side.\n");
  1646.     strcat(buf, "Windows\n");
  1647.     strcat(buf, "  This menu does window control.\n");
  1648.     strcat(buf, "View (Map)\n");
  1649.     strcat(buf, "  Items toggle various display elements in the front map window.\n");
  1650.     strcat(buf, "\n");
  1651.     strcat(buf, "View (List)\n");
  1652.     strcat(buf, "\n");
  1653. }
  1654.  
  1655. describe_mouse(arg, key, buf)
  1656. int arg;
  1657. char *key, *buf;
  1658. {
  1659.     strcat(buf, "In move-on-click mode:\n");
  1660.     strcat(buf, "  The next unit that can do anything will be selected automatically.\n");
  1661.     strcat(buf, "  Click once on a destination to move the selected unit there.\n");
  1662.     strcat(buf, "In normal mode:\n");
  1663.     strcat(buf, "  Click once on a unit to select it.  ");
  1664.     strcat(buf, "  Drag to destination to move it.  ");
  1665.     strcat(buf, "  Shift-click to select additional units, ");
  1666.     strcat(buf, "  Drag out rectangle to select all units inside.\n");
  1667. }
  1668.  
  1669. describe_shortcuts(arg, key, buf)
  1670. int arg;
  1671. char *key, *buf;
  1672. {
  1673.     strcat(buf, "Command-click moves all selected units to the clicked-on location.");
  1674. }
  1675.  
  1676. describe_help(arg, key, buf)
  1677. int arg;
  1678. char *key, *buf;
  1679. {
  1680.     strcat(buf, "Click on the obvious buttons.");
  1681.     strcat(buf, "\n");
  1682.     strcat(buf, "To select a topic in the topics list, click on it.");
  1683. }
  1684.  
  1685. create_help_window()
  1686. {
  1687.     int h, v;
  1688.     Rect helptopicrect, destrect, viewrect, vscrollrect;
  1689.  
  1690.     /* Create the window, color if possible, since images may be in color. */
  1691.     if (hasColorQD) {    
  1692.         helpwin = GetNewCWindow(wHelp, NULL, (WindowPtr) -1L);
  1693.     } else {
  1694.         helpwin = GetNewWindow(wHelp, NULL, (WindowPtr) -1L);
  1695.     }
  1696.     topicsbutton = GetNewControl(cTopicsButton, helpwin);
  1697.     helpbutton = GetNewControl(cHelpButton, helpwin);
  1698.     prevbutton = GetNewControl(cPrevButton, helpwin);
  1699.     nextbutton = GetNewControl(cNextButton, helpwin);
  1700.     backbutton = GetNewControl(cBackButton, helpwin);
  1701.     SetPort(helpwin);
  1702.     /* All text will be in Times. */
  1703.     /* (should these be choosable?) */
  1704.     TextFont(times);
  1705.     /* Set up the topic title. */
  1706.     TextSize(18);
  1707.     SetRect(&helptopicrect, 45, 45, 305, 75); 
  1708.     helptopic = TENew(&helptopicrect, &helptopicrect);
  1709.     /* Set up the help text proper. */
  1710.     TextSize(14);
  1711.     h = window_width(helpwin);  v = window_height(helpwin);
  1712.     SetRect(&viewrect, 5, 75, h - sbarwid, v - sbarwid); 
  1713.     destrect = viewrect;
  1714.     helptext = TENew(&destrect, &destrect);
  1715.     /* Set up a vertical scrollbar. */
  1716.     vscrollrect = helpwin->portRect;
  1717.     vscrollrect.top = 75;
  1718.     vscrollrect.bottom -= sbarwid - 1;
  1719.     vscrollrect.left = vscrollrect.right - sbarwid;
  1720.     vscrollrect.right += 1;
  1721.     helpvscrollbar =
  1722.         NewControl(helpwin, &vscrollrect, "\p", TRUE, 0, 0, 0, scrollBarProc, 0L);
  1723.     HiliteControl(helpvscrollbar, 0);
  1724.     /* Add the Mac-specific help nodes. */
  1725.     add_help_node("menus", describe_menus, 0, firsthelpnode);
  1726.     add_help_node("shortcuts", describe_shortcuts, 0, firsthelpnode);
  1727.     add_help_node("mouse input", describe_mouse, 0, firsthelpnode);
  1728.     helphelpnode = add_help_node("help", describe_help, 0, firsthelpnode);
  1729.     curhelpnode = firsthelpnode;
  1730.     set_help_content(curhelpnode);
  1731.     if (nodestack == NULL) {
  1732.         nodestack = (HelpNode **) xmalloc(NODESTACKSIZE * sizeof(HelpNode *));
  1733.     }
  1734.     nodestackpos = 0;
  1735.     add_window_menu_item("Help", helpwin);
  1736. }
  1737.  
  1738. set_help_content(curnode)
  1739. HelpNode *curnode;
  1740. {
  1741.     int i;
  1742.     char *str;
  1743.     Str255 tmpstr;
  1744.  
  1745.     get_help_text(curnode);
  1746.     /* Set the displayed topic title. */
  1747.     TESetSelect(0, 32767, helptopic);
  1748.     TECut(helptopic);
  1749.     /* Copy in the new help topic text. */
  1750.     TESetText(curnode->key, strlen(curnode->key), helptopic);
  1751.     /* Set the displayed text. */
  1752.     str = curnode->text;
  1753.     /* Hack up newlines so that TextEdit recognizes them. */
  1754.     for (i = 0; i < strlen(str); ++i) {
  1755.         if (str[i] == '\n') str[i] = '\r';
  1756.     }
  1757.     helpstring = str;
  1758.     /* Remove all the existing text. */
  1759.     TESetSelect(0, 32767, helptext);
  1760.     TECut(helptext);
  1761.     /* Copy in the new help text. */
  1762.     TESetText(helpstring, strlen(helpstring), helptext);
  1763.     (*helptext)->destRect = (*helptext)->viewRect;
  1764.     /* Update on the screen. */
  1765.     draw_help();
  1766.     adjust_help_scrollbar();
  1767. }
  1768.  
  1769. draw_help()
  1770. {
  1771.     Rect tmprect;
  1772.     GrafPtr oldport;
  1773.  
  1774.     GetPort(&oldport);
  1775.     SetPort(helpwin);
  1776.     SetRect(&tmprect, 5, 40, 5 + 32, 40 + 32);
  1777.     EraseRect(&tmprect);
  1778.     if (curhelpnode->nclass == utypenode && is_unit_type(curhelpnode->arg)) {
  1779.         draw_unit_image(helpwin, tmprect.left, tmprect.top, 32, 32,
  1780.                         curhelpnode->arg, -1, 0);
  1781.     } else if (curhelpnode->nclass == ttypenode && is_terrain_type(curhelpnode->arg)) {
  1782.         draw_terrain_sample(tmprect, curhelpnode->arg); 
  1783.     }
  1784.     TextSize(18);
  1785.     TEUpdate(&(helpwin->portRect), helptopic);
  1786.     TextSize(14);
  1787.     TEUpdate(&(helpwin->portRect), helptext);
  1788.     SetPort(oldport);
  1789.     adjust_help_scrollbar();
  1790. }
  1791.  
  1792. adjust_help_scrollbar()
  1793. {
  1794.     int lines, newmax, value;
  1795.  
  1796.     lines = (*helptext)->nLines;
  1797.     newmax = lines - (((*helptext)->viewRect.bottom - (*helptext)->viewRect.top)
  1798.                      / (*helptext)->lineHeight);
  1799.     if (newmax < 0) newmax = 0;
  1800.     SetCtlMax(helpvscrollbar, newmax);
  1801.     value = ((*helptext)->viewRect.top - (*helptext)->destRect.top)
  1802.             / (*helptext)->lineHeight;
  1803.     SetCtlValue(helpvscrollbar, value);
  1804. }
  1805.  
  1806. activate_help(activate)
  1807. int activate;
  1808. {
  1809.     HiliteControl(helpvscrollbar, (activate ? 0 : 255));
  1810. }
  1811.  
  1812. pascal void
  1813. help_vscroll_proc(control, code)
  1814. ControlHandle control;
  1815. short code;
  1816. {
  1817.     int curvalue, minvalue, maxvalue, pagesize, dir, jump;
  1818.  
  1819.     curvalue = GetCtlValue(control);
  1820.     minvalue = GetCtlMin(control);
  1821.     maxvalue = GetCtlMax(control);
  1822.     pagesize = ((*helptext)->viewRect.bottom - (*helptext)->viewRect.top) /
  1823.                 (*helptext)->lineHeight;
  1824.     switch (code) {
  1825.         case inPageDown:
  1826.             jump = pagesize;
  1827.             break;
  1828.         case inDownButton:
  1829.             jump = 1;
  1830.             break;
  1831.         case inPageUp:
  1832.             jump = - pagesize;
  1833.             break;
  1834.         case inUpButton:
  1835.             jump = -1;
  1836.             break;
  1837.         default:
  1838.             jump = 0;
  1839.             break;
  1840.     }
  1841.     if (curvalue + jump > maxvalue) jump = 0;
  1842.     if (curvalue + jump < minvalue) jump = 0;
  1843.     curvalue = max(min(curvalue + jump, maxvalue), minvalue);
  1844.     SetCtlValue(control, curvalue);
  1845.     if (jump != 0) TEScroll(0, - jump * (*helptext)->lineHeight, helptext);
  1846. }
  1847.  
  1848. /* Respond to an event occurring in the help window. */
  1849.  
  1850. do_mouse_down_help(mouse, mods)
  1851. Point mouse;
  1852. int mods;
  1853. {
  1854.     ControlHandle control;
  1855.     short part, value;
  1856.     int u, i;
  1857.     Point pt;
  1858.     Unit *unit;
  1859.     HelpNode *prevhelpnode = curhelpnode, *helpnode;
  1860.  
  1861.     part = FindControl(mouse, helpwin, &control);
  1862.     if (control == topicsbutton) {
  1863.         curhelpnode = firsthelpnode;
  1864.     } else if (control == helpbutton) {
  1865.         curhelpnode = helphelpnode;
  1866.     } else if (control == prevbutton) {
  1867.         curhelpnode = curhelpnode->prev;
  1868.     } else if (control == nextbutton) {
  1869.         curhelpnode = curhelpnode->next;
  1870.     } else if (control == backbutton) {
  1871.         if (nodestackpos > 0) {
  1872.             curhelpnode = nodestack[--nodestackpos];
  1873.         }
  1874.     } else if (control == helpvscrollbar) {
  1875.         if (part != 0) {
  1876.             switch (part) {
  1877.                 case inPageDown:
  1878.                 case inDownButton:
  1879.                 case inPageUp:
  1880.                 case inUpButton:
  1881.                     value = TrackControl(control, mouse, (ProcPtr) help_vscroll_proc);
  1882.                     break;
  1883.                 case inThumb:
  1884.                     value = GetCtlValue(control);
  1885.                     if ((part = TrackControl(control, mouse, nil)) != 0) {
  1886.                         value -= GetCtlValue(control);
  1887.                         if (value != 0) {
  1888.                             TEScroll(0, value * (*helptext)->lineHeight, helptext);
  1889.                         }
  1890.                     }
  1891.                     break;
  1892.             }
  1893.         }
  1894.     } else if (PtInRect(mouse, &((*helptext)->viewRect))) {
  1895.         TEClick(mouse, 0, helptext);
  1896.         if (curhelpnode == firsthelpnode) {
  1897.             char strbuf[100], *cr1, *cr2;
  1898.             int selstart = (*helptext)->selStart, selend = (*helptext)->selEnd;
  1899.             CharsHandle chars = TEGetText(helptext);
  1900.  
  1901.             if (selstart == selend) {
  1902.                 /* Manufacture a "selection" of the line clicked in. */
  1903.                 for (cr1 = (*chars)+selstart; cr1 != (*chars); --cr1) if (*cr1 == '\r') break;
  1904.                 cr2 = strchr((*chars)+selstart, '\r');
  1905.                 selstart = (cr1 != NULL ? cr1 - (*chars) + 1 : 0);
  1906.                 selend = (cr2 != NULL ? cr2 - (*chars) : 0);
  1907.                 if (selstart > selend) selstart = selend;
  1908.             }
  1909.             if (selstart != selend) {
  1910.                 strncpy(strbuf, (*chars)+selstart, selend - selstart);
  1911.                 strbuf[selend - selstart] = '\0';
  1912.                 helpnode = find_help_node(curhelpnode, strbuf);
  1913.                 if (helpnode != NULL) {
  1914.                     curhelpnode = helpnode;
  1915.                 } else {
  1916.                     SysBeep(20);
  1917.                 }
  1918.             } else {
  1919.                 SysBeep(20);
  1920.             }
  1921.         }
  1922.     }
  1923.     /* If we changed help nodes, get its contents and record on the node stack. */
  1924.     if (prevhelpnode != curhelpnode) {
  1925.         set_help_content(curhelpnode);
  1926.         if (control != backbutton) {
  1927.             if (nodestackpos >= NODESTACKSIZE) {
  1928.                 for (i = 1; i < NODESTACKSIZE; ++i) nodestack[i - 1] = nodestack[i];
  1929.                 nodestackpos = NODESTACKSIZE - 1;
  1930.             }
  1931.             nodestack[nodestackpos++] = prevhelpnode;
  1932.         }
  1933.     }
  1934.     return TRUE;
  1935. }
  1936.  
  1937. grow_help(h, v)
  1938. short h, v;
  1939. {
  1940.     EraseRect(&helpwin->portRect);
  1941.     SizeWindow(helpwin, h, v, 1);
  1942.     MoveControl(helpvscrollbar, h - sbarwid, 75);
  1943.     SizeControl(helpvscrollbar, sbarwid + 1, v - 75 - sbarwid + 1);
  1944.     (*helptext)->viewRect.right = h - sbarwid;
  1945.     (*helptext)->viewRect.bottom = v - sbarwid;
  1946.     (*helptext)->destRect.right = h - sbarwid;
  1947.     TECalText(helptext);
  1948.     InvalRect(&helpwin->portRect);
  1949. }                    
  1950.